package com.shareyi.simpleserver.core;

import java.io.*;
import java.util.*;
import java.net.*;



/**
 * This holds the http request data a given request.
 *@author David zhang
 *@since Tue 23 May 2011
 *@version 1.0
 */

public class HttpRequest extends Request {

    private String scheme;

    private String method;	//request method: post/get/head....

    private String url;		//request url

    private String query;	//the query data follow url

    private String protocol;

    private int major;		//the protocol main version

    private int minor;		//the protocol sub version

    private HttpHeaders headers;

    private byte[] postData; //the post datas

    private Map<String,Object> parameterMap; //parameters

    private Map<String,Object> attributeMap=new HashMap<String,Object>();//attributes
    
    private String connectionHeader;

    private long timeStamp;
    
    private String contextPath; //prefix of url,default is ""
    
    private String uri;		//uri of url
    
    private String postfix;	//postfix of url
	
    private String dispatcherType;
    
    
    public static final String GET_METHOD="GET";
    public static final String POST_METHOD="POST";
    public static final String HEAD_METHOD="HEAD";
    
    
    
    public HttpRequest( Socket aConnection) {
    	super(aConnection);
    	//default is request
    	dispatcherType=HttpConstant.DISPATCH_TYPE_REQUEST;
    	init();
    }
    
    
    public int readRequest( InputStream aStream ) throws IOException {
        InternetInputStream stream = new InternetInputStream( aStream );
        String startLine = null;
      //  try {
            startLine = readHttpCommand( stream );
            if( startLine == null ) {
                return HttpURLConnection.HTTP_BAD_REQUEST; //400-Bad Request
            }
            if ( protocol.equals("HTTP/1.0") ) {
                major = 1;
                minor = 0;
            } else if ( protocol.equals("HTTP/1.1") ) {
                major = 1;
                minor = 1;
            }
            else {
             return HttpURLConnection.HTTP_VERSION;	//505-HTTP Version not supported
            }
            
           
            if(HEAD_METHOD.equals(method))
            {
            	if("test_running.chk".equals(url))
            	{
            		headers = new HttpHeaders( stream );
            		return 1000;	//如果为测试是否在运行
            	}
            }
            
            if(!POST_METHOD.equals(method)&&!GET_METHOD.equals(method))
            	return HttpURLConnection.HTTP_BAD_METHOD; //405-Method Not Allowed
            
            headers = new HttpHeaders( stream );

            
            
            String contentType = headers.get("Content-Type");
            if(POST_METHOD.equals(method)&&contentType!=null&&contentType.indexOf("application/x-www-form-urlencoded")!=-1)
            {
            	int state=readPostData( stream );
            	if(state!=HttpURLConnection.HTTP_OK)
            		return state;
            }
            
            //生成参数数据
            if ( parameterMap == null ) {
                parameterMap = createQueryMap( query );
                if (postData != null) {
                    if (contentType!=null&&contentType.indexOf("application/x-www-form-urlencoded")!=-1) {
                    	String postDataStr= new String( postData );
                        parameterMap.putAll( createQueryMap( postDataStr) );
                    }
                }
            }
      
        return HttpURLConnection.HTTP_OK;	//200-OK
    }

    private void init() {
        method = null;
        url = null;
        query = null;
        protocol = null;
        postData = null;
        parameterMap = null;
        timeStamp = System.currentTimeMillis();
        connectionHeader = "Connection";
    }

  

    private String readHttpCommand( InternetInputStream stream ) throws IOException {
        String startLine  = null;
        do {
            startLine = stream.readline();
            if( startLine == null ) return null;
        } while( startLine.trim().length() == 0 );

        StringTokenizer tokenizer = new StringTokenizer( startLine );
        method = tokenizer.nextToken();
        parseUrl( tokenizer.nextToken());
        
        setNowUrl(url);
		protocol = tokenizer.nextToken();
        return startLine;
    }

    private int readPostData( InternetInputStream stream ) throws IOException {
        String contenLength = getRequestHeader("Content-Length");
        if( contenLength == null ) return HttpURLConnection.HTTP_LENGTH_REQUIRED; //411-Length Required

        try
        {
        int postLength = Integer.parseInt( contenLength);
        if(postLength>1024*1024)
        	return HttpURLConnection.HTTP_ENTITY_TOO_LARGE; //413-Request Entity Too Large 本系统只接收小于1M的数据
        postData = new byte[postLength];
    	}catch(NumberFormatException e)
    	{
    		return HttpURLConnection.HTTP_LENGTH_REQUIRED;//411-Length Required
    	}
    	
    	
        int length = -1;
        int offset = stream.read( postData );
        while( offset >= 0 && offset < postData.length ) {
            length = stream.read( postData, offset, postData.length - offset );
            if( length == -1 ) {
                break;
            }
            offset += length;
        }
        return HttpURLConnection.HTTP_OK; //OK
    }

    private void parseUrl( String aUrl ) {
        int queryIndex = aUrl.indexOf('?');
        if( queryIndex < 0 ) {
            url = aUrl;
        } else {
            url = aUrl.substring( 0, queryIndex );
            if( (queryIndex + 1) < aUrl.length() )
                query = aUrl.substring( queryIndex + 1 );
        }
    }

    public String getRequestHeader(String key) {
    	if(headers!=null){
    		return headers.get( key );
    	}
    	return null;
    }

    public String getRequestHeader(String key, String defaultValue ) {
        String val = getRequestHeader( key );
        return ( val == null ) ? defaultValue : val;
    }

    public String getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    
    public String getContextPath() {
		return contextPath;
	}


	public String getUri() {
		return uri;
	}


	public String getPostfix() {
		return postfix;
	}


	public String getQuery() {
        return query;
    }

    /**
     * get parameter value by key
     * @param key
     * @return parameter value
     */
    public String getParameter( String key ) {
        Object value= parameterMap.get( key );
        if(value==null){
            return null;
        }
        //如果为数组，则返回第一个值
        if(value.getClass().isArray()){
            return ((String[])value)[0];
        }else{
            return (String)value;
        }
    }


    /**
     * get parameter value by key
     * @param key
     * @return parameter value
     */
    public String[] getParameterValues( String key ) {
        Object value= parameterMap.get( key );
        if(value==null){
            return null;
        }
        //如果为数组，则返回第一个值
        if(value.getClass().isArray()){
            return (String[])value;
        }else{
            return new String[]{(String)value};
        }
    }

    /**
     * get parameter's keySet
     * @return keySet
     */
    public Set getParameterNames() {
        return parameterMap.keySet();
    }

    public Map<String,Object> getParameterMap( ) {
        return parameterMap;
    }
    
    private Map createQueryMap( String query ) {
        Map queryMap = new TreeMap();
        if (query == null) {
            return queryMap;
        }
        query=query.trim();
        if("".equals(query))
        	return queryMap;
        
        query = query.replace('+', ' ');
        query = query.replace('?', '&');
        StringTokenizer st = new StringTokenizer(query, "&");
        try {
            while (st.hasMoreTokens()) {
                String field = st.nextToken();
                int index = field.indexOf('=');
                String key=null;
                String value=null;
                if (index < 0) {
                    key=URLDecoder.decode( field, "UTF-8" );
                    value="";
                    queryMap.put( URLDecoder.decode( field, "UTF-8" ), "" );
                } else {
                    key=URLDecoder.decode( field.substring(0, index), "UTF-8");
                    value=URLDecoder.decode( field.substring(index + 1), "UTF-8");
                }

                Object oldValue=queryMap.get(key);
                if(oldValue==null){
                    queryMap.put(key,value);
                }else{
                    if(oldValue.getClass().isArray()){
                        String[] oldValueArray=(String[])oldValue;
                        String[] newValueArray=new String[oldValueArray.length+1];
                        for(int i=0;i<oldValueArray.length;i++){
                            newValueArray[i]=oldValueArray[i];
                        }
                        newValueArray[oldValueArray.length]=value;
                        queryMap.put(key,newValueArray);
                    }else{
                        String oldValueStr=(String)oldValue;
                        queryMap.put(key,new String[]{oldValueStr, value});
                    }
                }
            }
        } catch (UnsupportedEncodingException e) {}

        return queryMap;
    }
    
    
    
    

    public String getScheme() {
        return scheme;
    }

    public String getProtocol() {
        return protocol;
    }

    public byte[] getPostData() {
        return postData;
    }

    public Object getAttribute(String key)
    {
    	return attributeMap.get(key);
    }

    public Map<String,Object> getAttributeMap()
    {
    	return attributeMap;
    }

    
    public void setAttribute(String key,Object obj)
    {
    	 attributeMap.put(key,obj);
    }
    
    public boolean isKeepAlive() {
        if ("Keep-Alive".equalsIgnoreCase( getRequestHeader(connectionHeader) ) ) {
            return true;
        } else if ("close".equalsIgnoreCase( getRequestHeader(connectionHeader) ) ) {
            return false;
        } else if ( major >= 1 && minor > 0) {
            return true;
        } else {
            return false;
        }
    }

    public int getMajorVersion() {
        return major;
    }

    public int getMinorVersion() {
        return minor;
    }

    public String getConnectionHeader() {
        return connectionHeader;
    }

    public long getTimestamp() {
        return timeStamp;
    }



	public void setNowUrl(String nowUrl) {
	    int queryIndex = nowUrl.indexOf('?');
	    String urlTemp;
        if( queryIndex < 0 ) {
        	urlTemp = nowUrl;
        } else {
        	urlTemp = nowUrl.substring( 0, queryIndex );
            if( (queryIndex + 2) < nowUrl.length() )
               parameterMap.putAll(createQueryMap(nowUrl.substring( queryIndex + 1 )));
        }
        
         nowUrl=urlTemp;
         
        int lastIndexOfDot=nowUrl.lastIndexOf('.');
        int lastIndexOfLine=nowUrl.lastIndexOf('/'); 
    	if(lastIndexOfLine!=-1){
    		contextPath=nowUrl.substring(0,lastIndexOfLine);
    	}
    	if(lastIndexOfDot!=-1) {
        	uri=nowUrl.substring(lastIndexOfLine+1,lastIndexOfDot);
			if(lastIndexOfDot<(nowUrl.length()+1)) {
					postfix=nowUrl.substring(lastIndexOfDot+1);
				}
			else {
				postfix="";
			}
        }else{
        		uri=nowUrl.substring(lastIndexOfLine+1);
        	 	postfix="";
        	}
		this.url = nowUrl;
	}


	public HttpHeaders getHeaders() {
        return headers;
    }



    public String createUrl( String absolutePath ) throws IOException {
        return absolutePath;
    }

    public boolean isProtocolVersionLessThan( int aMajor, int aMinor ) {
        return ( major <= aMajor && minor < aMinor );
    }
    
    
    public String toString() {
        return method + " " + url + ( (query!=null) ? "?" + query : "" ) +  " " + protocol;
    }
    
    
    /**
     * 获取转向实例
     * 
     * @param location
     * @return
     */
    
    public RequestDispatcher getRequestDispacher(String location)
    {
    	this.setNowUrl(location);
		return new RequestDispatcher(location);
    	
    }


	public String getDispatcherType() {
		return dispatcherType;
	}


	protected void setDispatcherType(String dispatcherType) {
		this.dispatcherType = dispatcherType;
	}
    
    
	public String getHeader(String key) {
		return headers.get(key);
	}   
}
